﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace NNCore
{
    [Serializable]
    public class Layer
    {
        public Layer() { }

        private AFunction activationFunction;
        
        private AFunction outputFunction;

        [XmlIgnore]
        private Matrix output;

        private double[] threshold;

        private Matrix weightMatrix;

        private Matrix activivationValues;

        public AFunction ActivationFunction
        {
            get { return activationFunction; }
            set { activationFunction = value; }
        }


        public AFunction OutputFunction
        {
            get { return outputFunction; }
            set { outputFunction = value; }
        }

        [XmlIgnore]
        public Matrix Output
        {
            get { return output; }
            set { output = value; }
        }

        
        [XmlArray("Threshold")]
        public double[] Threshold
        {
            get { return threshold; }
            set { threshold = value; }
        }

        [XmlIgnore]
        public Matrix WeightMatrix
        {
            get { return weightMatrix; }
            set { weightMatrix = value; }
        }

        [XmlIgnore]
        public Matrix ActivationValues
        {
            get
            {
                return activivationValues;
            }
            set
            {
                activivationValues = value;
            }
        }

        [XmlArray("SerializedWeightMatrix")]
        public double[] SerializedWeightMatrix
        {
            get
            {
                if (WeightMatrix == null)
                {
                    return null;
                }

                int width = WeightMatrix.Width;
                int height = WeightMatrix.Height;

                double[] vector = WeightMatrix.Vector;
                double[] serializedWeights = new double[vector.Length + 2];

                for (int i = 0; i < vector.Length; i++)
                    serializedWeights[i] = vector[i];

                serializedWeights[(width * height)] = height;
                serializedWeights[(width * height) + 1] = width;
 
                return serializedWeights;
            }

            set
            {
                int width = (int)value[value.Length - 1];

                int height = (int)value[value.Length - 2];

                if (WeightMatrix == null)
                {
                    WeightMatrix = new Matrix(height, width);
                }

                double[] vector = new double[value.Length - 2];

                for (int i = 0; i < vector.Length; i++)
                    vector[i] = value[i];

                WeightMatrix.Vector = vector;
            }
        }

        public void computeActivation(Matrix nettoInput)
        {
            double[] activations = new double[nettoInput.Width];
            if (ActivationFunction == null)
            {
                for (int i = 0; i < nettoInput.Width; i++)
                {
                    activations[i] = nettoInput[0, i];
                }
            }
            else
            {

                for (int i = 0; i < nettoInput.Width; i++)
                {
                    activations[i] = ActivationFunction.compute(nettoInput[0, i], threshold[i]);
                }
            }

            ActivationValues = Matrix.createHorVector(activations);
        }

        public void computeOutput()
        {

            double[] outputs = new double[ActivationValues.Width];
            for (int i = 0; i < ActivationValues.Width; i++)
            {
                outputs[i] = OutputFunction.compute(ActivationValues[0, i], threshold[i]);
            }
            Output = Matrix.createHorVector(outputs);
        }

        public void ResetActivationValues()
        {
            ActivationValues = Matrix.createHorVector(new double[ActivationValues.Width]);
        }

    }
}
